K8SPS-409 | Encrypted backups#1243
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for encrypted backups/restores by introducing an encryption spec in backup storage configuration, wiring it through the backup sidecar, and updating restore flow to decrypt before prepare/move-back.
Changes:
- Add
encryptionconfiguration to Backup/Storage specs and propagate it into the sidecar backup request/config. - Implement encryption key handling in the sidecar (read key from Secret, write temp key file, pass
--encrypt-*flags to xtrabackup). - Introduce MySQL pod RBAC/ServiceAccount resources to allow in-pod Secret reads, and update CRDs/bundles/examples accordingly.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/xtrabackup/xtrabackup.go | Pass encryption options into backup jobs; mount secret + set ENCRYPTION_KEY_FILE for restore jobs; extend BackupConfig. |
| pkg/mysql/mysql.go | Add RBAC object constructors; conditionally set MySQL StatefulSet serviceAccountName for newer CR versions. |
| pkg/controller/ps/controller.go | Create Role/RoleBinding/ServiceAccount during DB reconcile for newer CR versions. |
| cmd/sidecar/main.go | Switch to constructing a backup handler via backup.NewHandler(). |
| cmd/sidecar/handler/handler.go | Remove backup handler factory wrapper. |
| cmd/sidecar/handler/backup/handler.go | Add handler constructor/init with a controller-runtime k8s client. |
| cmd/sidecar/handler/backup/create.go | Create temp encryption key file and pass encryption flags to xtrabackup. |
| build/run-backup.sh | Include encryption in the JSON payload sent to the sidecar. |
| build/run-restore.sh | Decrypt backup contents when ENCRYPTION_KEY_FILE is set. |
| api/v1/perconaservermysql_types.go | Add EncryptionSpec to BackupStorageSpec and implement Secret key reading helper. |
| api/v1/perconaservermysqlbackup_types.go | Add per-backup encryption override + storage fallback getter. |
| api/v1/zz_generated.deepcopy.go | Update deep-copy generation for new encryption types/fields. |
| config/crd/bases/*.yaml | Extend CRD OpenAPI schemas to include encryption blocks. |
| deploy/.yaml, deploy/backup/.yaml | Update shipped CRDs/bundles and example manifests with encryption fields. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 19 changed files in this pull request and generated 15 comments.
Files not reviewed (1)
- api/v1/zz_generated.deepcopy.go: Language not supported
Comments suppressed due to low confidence (2)
cmd/sidecar/handler/backup/create.go:111
- This
ifblock is a no-op and can be removed (the actual wait already happens earlier viaawaitEncryptionKeyFile). Leaving empty conditionals makes the control flow harder to follow.
xtrabackup.Env = envs(backupConf)
xbOut, err := xtrabackup.StdoutPipe()
cmd/sidecar/handler/backup/create.go:247
- The default
--encrypt=AES256flag is appended even when no encryption key is configured. That will cause previously-unencrypted backups to start failing (xtrabackup encryption requires a key or key file). Only set a default--encrypt=when encryption is actually enabled (e.g., whenEncryptionKeyFileis set).
}
return args
}
func getClusterType() apiv1.ClusterType {
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
| return ctrl.NewControllerManagedBy(mgr). | ||
| For(&apiv1.PerconaServerMySQL{}). | ||
| Watches(&corev1.Secret{}, enqueueClusterFromSecretsName(r.Client)). | ||
| Watches(&corev1.Secret{}, enqueueClusterFromEncryptionKeySecret(r.Client)). | ||
| Named("ps-controller"). | ||
| Complete(r) |
| "encryptionKeySecret": { | ||
| "name": "${secretName}", | ||
| "key": "encryptionKey" |
There was a problem hiding this comment.
Asking since I didn't confirm. If we change the encryption key after the backup, and try to perform a restore, what key are we going to use? Are we sure that the restorer is not mounting the new key?
There was a problem hiding this comment.
The restore fails in this case, since the original key is lost. If the user changes the encryption key, IMO they should take a new backup
egegunes
left a comment
There was a problem hiding this comment.
LGTM but please check copilot comments
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
commit: a5f2884 |
CHANGE DESCRIPTION
Problem:
Backups taken by the operator are stored unencrypted. Users storing backups in shared or external storage (e.g. S3) have no built-in way to protect them at rest.
Solution:
Adds support for creating and restoring encrypted backups using Percona XtraBackup's built-in encryption. Encryption is enabled by referencing a Secret that holds the encryption key. The default algorithm is
AES256; it can be changed via xtrabackup container args.Usage:
.spec.backup.encryptionKeySecret:CHECKLIST
Jira
Needs Doc) and QA (Needs QA)?Tests
Config/Logging/Testability